////////////////  JS 项目 兼容
const { ccclass, property } = cc._decorator;
@ccclass
export default class Http_js extends cc.Component {
    onLoad() {
        window["http"] = Http.get_instance();
    }
}

/**
 *        短链接
 */
const _string_errcode = "errcode"; // 错误代码字段名
const _string_errmsg = "errmsg"; // 错误代码 描述
const _succ_code = 0;  // 操作成功 代码
const _post_content_Type = "application/x-www-form-urlencoded"; //// POST body 数据类型
//const _post_content_Type = "application/json";
const _timeout = 5000;

const url = "http://120.26.171.101:9080/server/";

export class Http {
    private static _instance: Http = null;
    public static get_instance(): Http {
        if (this._instance == null) {
            this._instance = new Http();
        }
        return this._instance;
    }

    is_show_loading = true; //////显示loading    

    jwt = null;

    public GET(path: string, query: object, succ: Function = null, fail: Function = null): Promise<object> {
        if (this.jwt != null) {
            if (query == null) {
                query = {};
            }
            query["jwt"] = this.jwt; /////// 添加jwt
        }

        return new Promise<object>((resolve, reject) => {
            this._get_send(url, path, query, null, succ, fail)
                .then((c: object) => { resolve(c) })
                .catch((e: string) => {
                    // reject(e) 
                });
        });
    }
    public GETWithOutJwt(path: string, query: object, succ: Function = null, fail: Function = null): Promise<object> {
        if (this.jwt != null) {
            if (query == null) {
                query = {};
            }
        }

        return new Promise<object>((resolve, reject) => {
            this._get_send(url, path, query, null, succ, fail)
                .then((c: object) => { resolve(c) })
                .catch((e: string) => { reject(e) });
        });
    }

    public POST(path: string, query: any, body: any, succ: Function = null, fail: Function = null): Promise<object> {
        if (this.jwt != null) {
            if (query == null) {
                query = {};
            }
            query["jwt"] = this.jwt; /////// 添加jwt
            // if (body == null) {
            //     body = {};
            // }
            // body["jwt"] = this.jwt;
        }

        return new Promise<object>((resolve, reject) => {
            this._post_send(url, path, query, body, null, succ, fail)
                .then((c: object) => { resolve(c) })
                .catch((e: string) => {
                    // reject(e) 
                    console.error(e);
                });
        });
    }
    public POSTWithOutJwt(path: string, query: any, body: any, succ: Function = null, fail: Function = null): Promise<object> {
        if (this.jwt != null) {
            if (query == null) {
                query = {};
            }
            // query["jwt"] = this.jwt; /////// 添加jwt
            // if (body == null) {
            //     body = {};
            // }
            // body["jwt"] = this.jwt;
        }

        return new Promise<object>((resolve, reject) => {
            this._post_send(url, path, query, body, null, succ, fail)
                .then((c: object) => { resolve(c) })
                .catch((e: string) => { reject(e) });
        });
    }

    ////////////////////////////// GET请求封装
    public async _get_send(url: string, path: string, query: object, head: object, succ: Function = null, fail: Function = null): Promise<object> {
        let par: http_par = {
            type: "GET",
            url: url + path + "?" + Http.varParams(query),
            head: head,
        }

        let r = null;
        for (let i = 0; i < 4; i++) {/////////重试3次
            if (r == null) {
                try {
                    r = await this._send(par);
                } catch (error) { }
            } else {
                break;
            }
        }

        if (r == null) {
            this.toast("网络连接异常,请检查网络后重试!");
            let t = {};
            t[_string_errcode] = -9999;
            t[_string_errmsg] = "请求失败";
            throw new Error(JSON.stringify(t));
        } else {
            let ret = JSON.parse(r);
            switch (ret[_string_errcode]) {
                case _succ_code:
                    succ && succ(ret);
                    Http.handle_message(path, ret); /////处理器
                    return ret;
                default:
                    console.error("errcode ", ret[_string_errcode]);
                    console.error("errmsg ", ret[_string_errmsg]);
                    fail && fail(ret);
                    this.error_code_handle(ret);//////代码错误统一处理
                    throw new Error(r);
            }
        }
    }

    ///////////////////////////////// post请求封装
    public async _post_send(url: string, path: string, query: object, body: object, head: object, succ: Function = null, fail: Function = null): Promise<object> {
        let par: http_par = {
            type: "POST",
            url: url + path + "?" + Http.varParams(query),
            head: head,
        }

        if (body != null) {
            if (_post_content_Type.indexOf("application/json") > -1) {
                par.body = JSON.stringify(body);
            } else if (_post_content_Type.indexOf("application/x-www-form-urlencoded") > -1) {
                par.body = Http.varParams(body);
            }
        }

        let r = null;
        for (let i = 0; i < 4; i++) {/////////重试3次
            if (r == null) {
                try {
                    r = await this._send(par);
                } catch (error) { }
            } else {
                break;
            }
        }
        
        if (r == null) {
            this.toast("网络连接异常,请检查网络后重试");
            let t = {};
            t[_string_errcode] = -9999;
            t[_string_errmsg] = "请求失败";
            throw new Error(JSON.stringify(t));
        } else {
            let ret = JSON.parse(r);
            switch (ret[_string_errcode]) {
                case _succ_code:
                    succ && succ(ret);
                    Http.handle_message(path, ret); /////处理器
                    return ret;
                default:
                    console.error("errcode ", ret[_string_errcode]);
                    console.error("errmsg ", ret[_string_errmsg]);
                    fail && fail(ret);
                    this.error_code_handle(ret);//////代码错误统一处理
                    throw new Error(ret);
            }
        }
    }

    //////////////// 发送 封装
    private _send(par: http_par): Promise<string> {
        return new Promise<string>((resolve, reject) => {
            let xhr = new XMLHttpRequest();
            xhr.open(par.type, par.url, true);
            xhr.setRequestHeader("Content-Type", _post_content_Type);
            xhr.timeout = _timeout;
            if (par.head) {
                for (let i in par.head) {
                    xhr.setRequestHeader(i, par.head[i]);
                }
            }
            xhr.onload = () => {
                if (xhr.readyState == 4 && (xhr.status >= 200 && xhr.status < 400)) {
                    resolve(xhr.responseText);
                } else {
                    this.err_xhr_status_handle(xhr.status);
                    xhr.abort();
                }
            }
            let err_fun = (e) => {
                console.error(e);
                xhr.abort();
                reject(e);
            }
            xhr.ontimeout = err_fun;
            xhr.onabort = err_fun;
            xhr.onerror = err_fun;
            if (par.body) {
                xhr.send(par.body);
            } else {
                xhr.send();
            }
        })
    }


    /***************************************************************
     *       数据 处理器 (每次请求, 都会传给 所有处理器)
     ***************************************************************/
    static messageHandlers: Map<string, http_message> = new Map(); /////// 所有相应消息的组件
    public static add_handle(key: string, h: http_message) {
        if (this.messageHandlers.has(key)) {
            console.log("add_handle()  key已存在 " + key);
        } else {
            this.messageHandlers.set(key, h);
        }
    }

    public static del_handle(key: string) {
        if (this.messageHandlers.has(key)) {
            this.messageHandlers.delete(key);
        } else {
            console.error("del_handle()  key 没有找到 " + key);
        }
    }

    public static clean_handle() {
        this.messageHandlers.clear();
    }

    public static handle_message(path: string, d: object) {
        this.messageHandlers.forEach((value, key) => {
            value({
                path: path,
                data: d
            });
        });
    }


    /****************************************************
     *    HTTP 请求异常  根据 status 处理
     ****************************************************/
    /**
     *   通用 错误 代码处理
     */
    private err_xhr_status_handle(xhr_status: number) {
        console.log('http xhr_status========>', xhr_status)
        switch (xhr_status) {
            case 404:

                break;
            default:
                break;
        }
    }

    /****************************************************
     *     请求成功 , 但是 错误码 不等于 0
     ****************************************************/
    private error_code_handle(c: object) {
        let code = c[_string_errcode];
        let msg = c[_string_errmsg];

        console.error(code, msg);

        this.toast(msg);
    }


    private toast(text: string) {
        // Toast.show(text);
        this._toast(text);
    }

    ////////////////// 对象 转换 URL 参数
    // public static varParams(data: object) {
    //     if (data == null) return "";//////没有参数

    //     let str = "?";
    //     for (let k in data) {
    //         if (str != "?") {
    //             str += "&";
    //         }
    //         str += k + "=" + data[k];
    //     }
    //     if (str == "?") { //////没有参数
    //         return "";
    //     }
    //     return encodeURI(str);
    // }

    public static varParams(data: object): string {
        if (data == null) return "";//////没有参数

        let str = "";
        for (let k in data) {
            if (str !== "") {
                str += "&";
            }
            str += encodeURIComponent(k) + "=" + encodeURIComponent(data[k]);
        }
        if (str === "?") { //////没有参数
            return "";
        }
        return str;
    }


    //////////////////////////// 自带Toast
    private _toast(text: string, gravity = "BOTTOM", duration = 3, bg_color = cc.color(102, 102, 102, 200)) {
        if (typeof text != "string") {
            return;
        }
        // canvas
        let canvas = cc.director.getScene().getComponentInChildren(cc.Canvas);
        let width = canvas.node.width;
        let height = canvas.node.height;
        let bgNode = new cc.Node();
        // bgNode.group = "UI";

        // Lable文本格式设置
        let textNode = new cc.Node();
        let textLabel = textNode.addComponent(cc.Label);
        textLabel.horizontalAlign = cc.Label.HorizontalAlign.CENTER;
        textLabel.verticalAlign = cc.Label.VerticalAlign.CENTER;
        textLabel.fontSize = 30;
        textLabel.string = text;

        // 当文本宽度过长时，设置为自动换行格式
        if (text.length * textLabel.fontSize > (width * 3) / 5) {
            textNode.width = (width * 3) / 5;
            textLabel.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
        } else {
            textNode.width = text.length * textLabel.fontSize;
        }
        let lineCount =
            ~~((text.length * textLabel.fontSize) / ((width * 3) / 5)) + 1;
        textNode.height = textLabel.fontSize * lineCount;

        // 背景设置
        let ctx = bgNode.addComponent(cc.Graphics);
        ctx.arc(
            -textNode.width / 2,
            0,
            textNode.height / 2 + 20,
            0.5 * Math.PI,
            1.5 * Math.PI,
            true
        );
        ctx.lineTo(textNode.width / 2, -(textNode.height / 2 + 20));
        ctx.arc(
            textNode.width / 2,
            0,
            textNode.height / 2 + 20,
            1.5 * Math.PI,
            0.5 * Math.PI,
            true
        );
        ctx.lineTo(-textNode.width / 2, textNode.height / 2 + 20);
        ctx.fillColor = bg_color;
        ctx.fill();

        bgNode.addChild(textNode);

        // gravity 设置Toast显示的位置
        if (gravity === "CENTER") {
            bgNode.y = 0;
            bgNode.x = 0;
        } else if (gravity === "TOP") {
            bgNode.y = bgNode.y + (height / 5) * 2;
        } else if (gravity === "BOTTOM") {
            bgNode.y = bgNode.y - (height / 5) * 2;
        }

        bgNode.zIndex = cc.macro.MAX_ZINDEX;
        canvas.node.addChild(bgNode);


        let finished = cc.callFunc(function () {
            bgNode.removeFromParent();
        });
        let action = cc.sequence(
            cc.moveBy(duration, cc.v2(0, 0)),
            cc.fadeOut(0.3),
            finished
        );
        bgNode.runAction(action);
    }
}

////////////////// http 参数接口
interface http_par {
    type: "GET" | "POST";
    url: string;
    body?: string;
    head?: object;
}


export interface http_message {
    (d: { path: string, data: object }): void;
}